use crate::document::DocumentManager;
use codegraph_core::{GraphSystem, Result as CoreResult};
use tower_lsp::lsp_types::{
    DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, Position, Range,
    SymbolInformation, SymbolKind, Url, WorkspaceSymbolParams, Location,
};
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, warn};

/// Provides document and workspace symbols
pub struct SymbolsProvider {
    document_manager: Arc<DocumentManager>,
    graph_system: Arc<RwLock<Option<GraphSystem>>>,
}

impl SymbolsProvider {
    pub fn new(
        document_manager: Arc<DocumentManager>,
        graph_system: Arc<RwLock<Option<GraphSystem>>>,
    ) -> Self {
        Self {
            document_manager,
            graph_system,
        }
    }

    pub async fn provide_document_symbols(
        &self,
        params: &DocumentSymbolParams,
    ) -> CoreResult<Option<DocumentSymbolResponse>> {
        let uri = &params.text_document.uri;

        debug!("Providing document symbols for {}", uri);

        // Get document
        let document = match self.document_manager.get_document(uri) {
            Some(doc) => doc,
            None => {
                warn!("Document not found for symbols: {}", uri);
                return Ok(None);
            }
        };

        let (language, content) = {
            let doc = document.read().await;
            let language = match &doc.language {
                Some(lang) => lang.clone(),
                None => {
                    debug!("No language detected for symbols");
                    return Ok(None);
                }
            };
            (language, doc.content.clone())
        };

        // Extract symbols from the document
        let symbols = self.extract_document_symbols(&content, &language, uri)?;

        if symbols.is_empty() {
            Ok(None)
        } else {
            Ok(Some(DocumentSymbolResponse::Nested(symbols)))
        }
    }

    pub async fn provide_workspace_symbols(
        &self,
        params: &WorkspaceSymbolParams,
    ) -> CoreResult<Option<Vec<SymbolInformation>>> {
        let query = &params.query;

        debug!("Providing workspace symbols for query: '{}'", query);

        // TODO: Search across all documents in the workspace
        // For now, search in all open documents
        let mut symbols = Vec::new();

        for uri in self.document_manager.list_documents() {
            if let Some(doc_symbols) = self.get_symbols_from_document(&uri, query).await? {
                symbols.extend(doc_symbols);
            }
        }

        if symbols.is_empty() {
            Ok(None)
        } else {
            Ok(Some(symbols))
        }
    }

    fn extract_document_symbols(
        &self,
        content: &str,
        language: &str,
        _uri: &Url,
    ) -> CoreResult<Vec<DocumentSymbol>> {
        let mut symbols = Vec::new();
        let lines: Vec<&str> = content.lines().collect();

        for (line_num, line) in lines.iter().enumerate() {
            if let Some(symbol) = self.parse_symbol(line, line_num, language) {
                symbols.push(symbol);
            }
        }

        Ok(symbols)
    }

    fn parse_symbol(&self, line: &str, line_num: usize, language: &str) -> Option<DocumentSymbol> {
        let trimmed = line.trim();
        
        // Skip comments and empty lines
        if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
            return None;
        }

        match language {
            "python" => self.parse_python_symbol(line, line_num),
            "javascript" | "typescript" => self.parse_javascript_symbol(line, line_num),
            "rust" => self.parse_rust_symbol(line, line_num),
            _ => None,
        }
    }

    fn parse_python_symbol(&self, line: &str, line_num: usize) -> Option<DocumentSymbol> {
        let trimmed = line.trim();

        // Function definition
        if trimmed.starts_with("def ") {
            if let Some(name) = self.extract_function_name(trimmed, "def ") {
                return Some(self.create_document_symbol(
                    name,
                    SymbolKind::FUNCTION,
                    line,
                    line_num,
                ));
            }
        }

        // Class definition
        if trimmed.starts_with("class ") {
            if let Some(name) = self.extract_class_name(trimmed, "class ") {
                return Some(self.create_document_symbol(
                    name,
                    SymbolKind::CLASS,
                    line,
                    line_num,
                ));
            }
        }

        // Variable assignment (simple heuristic)
        if trimmed.contains(" = ") && !trimmed.contains("==") && !trimmed.starts_with("def ") && !trimmed.starts_with("class ") {
            if let Some(name) = trimmed.split(" = ").next() {
                let clean_name = name.trim();
                if self.is_valid_identifier(clean_name) {
                    return Some(self.create_document_symbol(
                        clean_name.to_string(),
                        SymbolKind::VARIABLE,
                        line,
                        line_num,
                    ));
                }
            }
        }

        None
    }

    fn parse_javascript_symbol(&self, line: &str, line_num: usize) -> Option<DocumentSymbol> {
        let trimmed = line.trim();

        // Function definition
        if trimmed.starts_with("function ") {
            if let Some(name) = self.extract_function_name(trimmed, "function ") {
                return Some(self.create_document_symbol(
                    name,
                    SymbolKind::FUNCTION,
                    line,
                    line_num,
                ));
            }
        }

        // Class definition
        if trimmed.starts_with("class ") {
            if let Some(name) = self.extract_class_name(trimmed, "class ") {
                return Some(self.create_document_symbol(
                    name,
                    SymbolKind::CLASS,
                    line,
                    line_num,
                ));
            }
        }

        // Arrow function or variable
        if trimmed.starts_with("const ") || trimmed.starts_with("let ") || trimmed.starts_with("var ") {
            if let Some(name) = self.extract_variable_name(trimmed) {
                let kind = if trimmed.contains("=>") || trimmed.contains("function") {
                    SymbolKind::FUNCTION
                } else {
                    SymbolKind::VARIABLE
                };
                return Some(self.create_document_symbol(name, kind, line, line_num));
            }
        }

        None
    }

    fn parse_rust_symbol(&self, line: &str, line_num: usize) -> Option<DocumentSymbol> {
        let trimmed = line.trim();

        // Function definition
        if trimmed.starts_with("fn ") {
            if let Some(name) = self.extract_function_name(trimmed, "fn ") {
                return Some(self.create_document_symbol(
                    name,
                    SymbolKind::FUNCTION,
                    line,
                    line_num,
                ));
            }
        }

        // Struct definition
        if trimmed.starts_with("struct ") {
            if let Some(name) = self.extract_type_name(trimmed, "struct ") {
                return Some(self.create_document_symbol(
                    name,
                    SymbolKind::STRUCT,
                    line,
                    line_num,
                ));
            }
        }

        // Enum definition
        if trimmed.starts_with("enum ") {
            if let Some(name) = self.extract_type_name(trimmed, "enum ") {
                return Some(self.create_document_symbol(
                    name,
                    SymbolKind::ENUM,
                    line,
                    line_num,
                ));
            }
        }

        // Trait definition
        if trimmed.starts_with("trait ") {
            if let Some(name) = self.extract_type_name(trimmed, "trait ") {
                return Some(self.create_document_symbol(
                    name,
                    SymbolKind::INTERFACE,
                    line,
                    line_num,
                ));
            }
        }

        // Variable binding
        if trimmed.starts_with("let ") {
            if let Some(name) = self.extract_rust_variable_name(trimmed) {
                return Some(self.create_document_symbol(
                    name,
                    SymbolKind::VARIABLE,
                    line,
                    line_num,
                ));
            }
        }

        None
    }

    fn extract_function_name(&self, line: &str, prefix: &str) -> Option<String> {
        if let Some(rest) = line.strip_prefix(prefix) {
            if let Some(paren_pos) = rest.find('(') {
                let name = rest[..paren_pos].trim();
                if self.is_valid_identifier(name) {
                    return Some(name.to_string());
                }
            }
        }
        None
    }

    fn extract_class_name(&self, line: &str, prefix: &str) -> Option<String> {
        if let Some(rest) = line.strip_prefix(prefix) {
            let name = rest
                .split_whitespace()
                .next()?
                .split('(')
                .next()?
                .split(':')
                .next()?
                .trim();
            if self.is_valid_identifier(name) {
                return Some(name.to_string());
            }
        }
        None
    }

    fn extract_type_name(&self, line: &str, prefix: &str) -> Option<String> {
        if let Some(rest) = line.strip_prefix(prefix) {
            let name = rest
                .split_whitespace()
                .next()?
                .split('<')
                .next()?
                .split('{')
                .next()?
                .trim();
            if self.is_valid_identifier(name) {
                return Some(name.to_string());
            }
        }
        None
    }

    fn extract_variable_name(&self, line: &str) -> Option<String> {
        // Extract variable name from declarations like "const name = ..."
        let parts: Vec<&str> = line.split_whitespace().collect();
        if parts.len() >= 3 && parts[1] != "=" {
            let name = parts[1].trim();
            if self.is_valid_identifier(name) {
                return Some(name.to_string());
            }
        }
        None
    }

    fn extract_rust_variable_name(&self, line: &str) -> Option<String> {
        // Extract variable name from "let name = ..." or "let mut name = ..."
        if let Some(rest) = line.strip_prefix("let ") {
            let rest = if rest.starts_with("mut ") {
                rest.strip_prefix("mut ")?
            } else {
                rest
            };
            
            let name = rest
                .split('=')
                .next()?
                .split(':')
                .next()?
                .trim();
            
            if self.is_valid_identifier(name) {
                return Some(name.to_string());
            }
        }
        None
    }

    fn is_valid_identifier(&self, name: &str) -> bool {
        !name.is_empty() && 
        name.chars().all(|c| c.is_alphanumeric() || c == '_') &&
        !name.chars().next().unwrap().is_ascii_digit()
    }

    fn create_document_symbol(
        &self,
        name: String,
        kind: SymbolKind,
        line: &str,
        line_num: usize,
    ) -> DocumentSymbol {
        let start_col = line.find(&name).unwrap_or(0);
        let range = Range::new(
            Position::new(line_num as u32, start_col as u32),
            Position::new(line_num as u32, (start_col + name.len()) as u32),
        );

        DocumentSymbol {
            name: name.clone(),
            detail: Some(line.trim().to_string()),
            kind,
            tags: None,
            #[allow(deprecated)]
            deprecated: None,
            range,
            selection_range: range,
            children: None,
        }
    }

    async fn get_symbols_from_document(
        &self,
        uri: &Url,
        query: &str,
    ) -> CoreResult<Option<Vec<SymbolInformation>>> {
        let document = match self.document_manager.get_document(uri) {
            Some(doc) => doc,
            None => return Ok(None),
        };

        let (language, content) = {
            let doc = document.read().await;
            let language = match &doc.language {
                Some(lang) => lang.clone(),
                None => return Ok(None),
            };
            (language, doc.content.clone())
        };

        let document_symbols = self.extract_document_symbols(&content, &language, uri)?;
        let mut symbols = Vec::new();

        for doc_symbol in document_symbols {
            if query.is_empty() || doc_symbol.name.to_lowercase().contains(&query.to_lowercase()) {
                symbols.push(SymbolInformation {
                    name: doc_symbol.name,
                    kind: doc_symbol.kind,
                    tags: doc_symbol.tags,
                    #[allow(deprecated)]
                    deprecated: doc_symbol.deprecated,
                    location: Location::new(uri.clone(), doc_symbol.range),
                    container_name: None,
                });
            }
        }

        if symbols.is_empty() {
            Ok(None)
        } else {
            Ok(Some(symbols))
        }
    }
}
